using System;
using System.Collections;
using System.Xml;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Reflection;
using NetFramework.Configuration;
using NetFramework.Diagnostics;

namespace NetFramework.DataAccess
{
	/// <summary>
	/// Each DataSource has one DataOperationFactory and delegates work to it.
	/// Returns DataCommands/DataSetAdapters from the cache.
	/// Maintains/refreshes internal DataCommand/DataSetAdapters caches.
	/// Config info retrieved from dataOperationsDir/dataOperationsFileMask from the DataSource.
	/// </summary>
	class DataOperationFactory
	{
		private static Hashtable _commands = null;
		private static Hashtable _adapters = null;

        public static void Init(Hashtable allDataSource)
        {
            _commands = new Hashtable();
            _adapters = new Hashtable();

            try
            {

                dataOperations dops = ConfigManager.XmlConfig.GetConfig<dataOperations>();
                Ensure.IstNull<dataOperations>(dops, "dataOperations");

                IDictionaryEnumerator er = allDataSource.GetEnumerator();
                while (er.MoveNext())
                {
                    DataSource ds = er.Value as DataSource;
                    if (dops.dataCommands != null)
                    {
                        foreach (dataCommand dc in dops.dataCommands)
                        {
                            if (dc.DataSource == ds.Name)
                            {
                                IDataCommand currentDataCommand = CreateDataCommand(ds, dc);
                                _commands.Add(currentDataCommand.Name, currentDataCommand);
                            }
                        }
                    }
                    if (dops.dataSetAdapters != null)
                    {
                        foreach (dataSetAdapter dsa in dops.dataSetAdapters)
                        {
                            if (dsa.DataSource == ds.Name)
                            {
                                IDataSetAdapter dataSetAdapter = CreateDataSetAdapter(ds, dsa);
                                _adapters.Add(dataSetAdapter.Name, dataSetAdapter);
                            }
                        }
                    }
                }
            }
            catch(Exception e)
            {
                throw new DataAccessException("Error loading cache from config.", e);
            }
        }

		private static IDataCommand CreateDataCommand(DataSource ds, dataCommand dc)
		{
			IDbCommand currentDbCommand =
                (IDbCommand)Activator.CreateInstance(ds.Provider.CommandObjectType);

			DataCommand currentDataCommand = new DataCommand(dc.name, currentDbCommand,
                ds);

			CommandType commandType = 
				(CommandType)Enum.Parse(typeof(CommandType), dc.type , true);
			currentDbCommand.CommandType = commandType;

			currentDbCommand.CommandText = dc.commandText.Trim('\t','\r','\n');

			if(dc.replaceByParamValues != null)
			{
				string defaultString;
				foreach(replaceByParamValue rbp in dc.replaceByParamValues)
				{
					defaultString = rbp.defaultString;
					if(defaultString != null)
						defaultString = defaultString.Trim('\t','\r','\n');

					currentDataCommand.AddReplaceByParamValue(
						rbp.paramName, 
						rbp.paramValue,
						rbp.oldString.Trim('\t','\r','\n'),
						rbp.newString.Trim('\t','\r','\n'),
						defaultString);
				}
			}

			if(dc.parameters != null)
			{
				foreach(param p in dc.parameters)
				{
					if(p.key == null) p.key = string.Empty;

					System.Enum paramType =
                        (System.Enum)Enum.Parse(ds.Provider.ParameterDbType, p.type, true);

					ParameterDirection paramDirection
						= (ParameterDirection)Enum.Parse(
						typeof(ParameterDirection), p.direction);;
									
					DataRowVersion sourceVersion = DataRowVersion.Default;
					if(p.sourceVersion != null) 
						sourceVersion = (DataRowVersion)Enum.Parse(
							typeof(DataRowVersion), p.sourceVersion, true);

					currentDataCommand.Parameters.Add(p.key, p.name, 
						paramType, p.size, paramDirection, p.sourceColumn, 
						sourceVersion, p.isNullable, (byte)p.scale);
				}
			}
			else if(dc.populateParameters)
			{
				//TODO: fix this hack
                //IDataCommand oldCmd = currentDataCommand;
                //currentDataCommand = this.DeriveCommand(
                //    currentDataCommand.DbCommand.CommandText);
                //currentDataCommand.Name = oldCmd.Name;
			}

			//set timeout if specified at command/datasource level
			if(dc.timeout != -1)
				currentDataCommand.DbCommand.CommandTimeout = dc.timeout;
			else if(ds.CommandTimeout != -1)
				currentDataCommand.DbCommand.CommandTimeout = 
					ds.CommandTimeout;

			return currentDataCommand;
		}

		private static IDataSetAdapter CreateDataSetAdapter(DataSource ds, dataSetAdapter dsa)
		{
			IDbDataAdapter currentDbDataAdapter = 
				(IDbDataAdapter)Activator.CreateInstance(ds.Provider.DataAdapterObjectType);
			IDataSetAdapter currentDataSetAdapter = 
				new DataSetAdapter(dsa.name, currentDbDataAdapter, ds);

			currentDataSetAdapter.SelectCommand = CreateDataCommand(ds, dsa.selectCommand);
			if(dsa.insertCommand != null)
                currentDataSetAdapter.InsertCommand = CreateDataCommand(ds, dsa.insertCommand);
			if(dsa.updateCommand != null)
                currentDataSetAdapter.UpdateCommand = CreateDataCommand(ds, dsa.updateCommand);
			if(dsa.deleteCommand != null)
                currentDataSetAdapter.DeleteCommand = CreateDataCommand(ds, dsa.deleteCommand);

			if(dsa.tableMappings != null)
			{
				foreach(tableMapping tm in dsa.tableMappings)
				{
					DataTableMapping dtm = currentDataSetAdapter.TableMappings.Add(
						tm.sourceTable, tm.dataSetTable);
					if(tm.columnMappings != null)
					{
						foreach(columnMapping cm in tm.columnMappings)
						{
							dtm.ColumnMappings.Add(cm.sourceColumn, cm.dataSetColumn);
						}
					}
				}
			}

			if(dsa.populateCommands)
				currentDataSetAdapter.PopulateCommands();

			return currentDataSetAdapter;
		}

		public static IDataSetAdapter GetAdapter(string adapterName) 
		{
			DataSetAdapter adapter = _adapters[adapterName] as DataSetAdapter;
			if(adapter == null) 
			{
				throw new DataAccessException("DataSetAdapter with name " + adapterName + " not found.");
			}

			DataSetAdapter newAdapter = (DataSetAdapter)adapter.Clone();

			return newAdapter;
		}

         public static IDataCommand GetCommand(string commandName)
        {
            DataCommand cmd = _commands[commandName] as DataCommand;
            if (cmd == null)
            {
                throw new DataAccessException("DataCommand with name " + commandName + " not found.");
            }

            DataCommand newCmd = (DataCommand)cmd.Clone();

            return newCmd;
        }
	}
}
